// Burner DipSwitches Dialog module
#include "burner.h"

HWND hInpDIPSWDlg = NULL;									// Handle to the DIPSW Dialog
static HWND hInpDIPSWList = NULL;

static unsigned char nPrevDIPSettings[8];

static unsigned int nDIPGroup;
static int nDIPOffset;

static bool bOK;

static void InpDIPSWGetOffset()
{
    BurnDIPInfo bdi;

    nDIPOffset = 0;
    for (int i = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++)
    {
        if (bdi.nFlags == 0xF0)
        {
            nDIPOffset = bdi.nInput;
            break;
        }
    }
}

void InpDIPSWResetDIPs()
{
    int i = 0;
    BurnDIPInfo bdi;
    struct GameInp *pgi;

    InpDIPSWGetOffset();

    while (BurnDrvGetDIPInfo(&bdi, i) == 0)
    {
        if (bdi.nFlags == 0xFF)
        {
            pgi = GameInp + bdi.nInput + nDIPOffset;
            pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
        }
        i++;
    }
}

static int InpDIPSWListBegin()
{
    LVCOLUMN LvCol;
    if (hInpDIPSWList == NULL)
    {
        return 1;
    }

    // Full row select style:
    SendMessage(hInpDIPSWList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
    // Make column headers
    memset(&LvCol, 0, sizeof(LvCol));
    LvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
    LvCol.cx = 158;
    LvCol.pszText = FBALoadStringEx(hAppInst, IDS_DIP_GROUP, true);
    SendMessage(hInpDIPSWList, LVM_INSERTCOLUMN, 0, (LPARAM)&LvCol);
    LvCol.cx = 215;
    LvCol.pszText = FBALoadStringEx(hAppInst, IDS_DIP_SETTING, true);
    SendMessage(hInpDIPSWList, LVM_INSERTCOLUMN, 1, (LPARAM)&LvCol);

    return 0;
}

static bool CheckSetting(int i)
{
    BurnDIPInfo bdi;
    BurnDrvGetDIPInfo(&bdi, i);
    struct GameInp *pgi = GameInp + bdi.nInput + nDIPOffset;

    if ((pgi->Input.Constant.nConst & bdi.nMask) == bdi.nSetting)
    {
        unsigned char nFlags = bdi.nFlags;
        if ((nFlags & 0x0F) <= 1)
        {
            return true;
        }
        else
        {
            for (int j = 1; j < (nFlags & 0x0F); j++)
            {
                BurnDrvGetDIPInfo(&bdi, i + j);
                pgi = GameInp + bdi.nInput + nDIPOffset;
                if (nFlags & 0x80)
                {
                    if ((pgi->Input.Constant.nConst & bdi.nMask) == bdi.nSetting)
                    {
                        return false;
                    }
                }
                else
                {
                    if ((pgi->Input.Constant.nConst & bdi.nMask) != bdi.nSetting)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    }
    return false;
}

// Make a list view of the DIPswitches
static int InpDIPSWListMake()
{
    if (hInpDIPSWList == NULL)
    {
        return 1;
    }
    SendMessage(hInpDIPSWList, LVM_DELETEALLITEMS, 0, 0);

    BurnDIPInfo bdi;
    unsigned int i = 0, j = 0, k = 0;
    char *pDIPGroup = NULL;
    while (BurnDrvGetDIPInfo(&bdi, i) == 0)
    {
        if ((bdi.nFlags & 0xF0) == 0xF0)
        {
            if (bdi.nFlags == 0xFE || bdi.nFlags == 0xFD)
            {
                pDIPGroup = bdi.szText;
                k = i;
            }
            i++;
        }
        else
        {
            if (CheckSetting(i))
            {
                LVITEM LvItem;
                memset(&LvItem, 0, sizeof(LvItem));
                LvItem.mask = LVIF_TEXT | LVIF_PARAM;
                LvItem.iItem = j;
                LvItem.iSubItem = 0;
                LvItem.pszText = ANSIToTCHAR(pDIPGroup, NULL, 0);
                LvItem.lParam = (LPARAM)k;
                SendMessage(hInpDIPSWList, LVM_INSERTITEM, 0, (LPARAM)&LvItem);
                LvItem.mask = LVIF_TEXT;
                LvItem.iSubItem = 1;
                LvItem.pszText = ANSIToTCHAR(bdi.szText, NULL, 0);
                SendMessage(hInpDIPSWList, LVM_SETITEM, 0, (LPARAM)&LvItem);
                j++;
            }
            i += (bdi.nFlags & 0x0F);
        }
    }

    return 0;
}

static int InpDIPSWInit()
{
    BurnDIPInfo bdi;
    struct GameInp *pgi;

    InpDIPSWGetOffset();

    hInpDIPSWList = GetDlgItem(hInpDIPSWDlg, IDC_INPCHEAT_LIST);
    InpDIPSWListBegin();
    InpDIPSWListMake();

    for (int i = 0, j = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++)
    {
        if (bdi.nInput >= 0  && bdi.nFlags == 0xFF)
        {
            pgi = GameInp + bdi.nInput + nDIPOffset;
            nPrevDIPSettings[j] = pgi->Input.Constant.nConst;
            j++;
        }
    }

    return 0;
}

static int InpDIPSWExit()
{
    hInpDIPSWList = NULL;
    hInpDIPSWDlg = NULL;
    if(!bAltPause && bRunPause)
    {
        bRunPause = 0;
    }
    GameInpCheckMouse();
    return 0;
}

static void InpDIPSWCancel()
{
    if (!bOK)
    {
        int i = 0, j = 0;
        BurnDIPInfo bdi;
        struct GameInp *pgi;
        while (BurnDrvGetDIPInfo(&bdi, i) == 0)
        {
            if (bdi.nInput >= 0 && bdi.nFlags == 0xFF)
            {
                pgi = GameInp + bdi.nInput + nDIPOffset;
                pgi->Input.Constant.nConst = nPrevDIPSettings[j];
                j++;
            }
            i++;
        }
    }
}

// Create the list of possible values for a DIPswitch
static void InpDIPSWSelect()
{
    SendMessage(GetDlgItem(hInpDIPSWDlg, IDC_INPCX1_VALUE), CB_RESETCONTENT, 0, 0);

    int nSel = SendMessage(hInpDIPSWList, LVM_GETNEXTITEM, (WPARAM) - 1, LVNI_SELECTED);
    if (nSel >= 0)
    {
        LVITEM LvItem;
        memset(&LvItem, 0, sizeof(LvItem));
        LvItem.mask = LVIF_PARAM;
        LvItem.iItem = nSel;
        SendMessage(hInpDIPSWList, LVM_GETITEM, 0, (LPARAM)&LvItem);

        nDIPGroup = LvItem.lParam;

        BurnDIPInfo bdiGroup;
        BurnDrvGetDIPInfo(&bdiGroup, nDIPGroup);

        int nCurrentSetting = 0;
        for (int i = 0, j = 0; i < bdiGroup.nSetting; i++)
        {
            TCHAR szText[256];
            BurnDIPInfo bdi;

            do
            {
                BurnDrvGetDIPInfo(&bdi, nDIPGroup + 1 + j++);
            }
            while (bdi.nFlags == 0);

            if (bdiGroup.szText)
            {
                _stprintf(szText, _T("%hs: %hs"), bdiGroup.szText, bdi.szText);
            }
            else
            {
                _stprintf(szText, _T("%hs"), bdi.szText);
            }
            SendMessage(GetDlgItem(hInpDIPSWDlg, IDC_INPCX1_VALUE), CB_ADDSTRING, 0, (LPARAM)szText);

            if (CheckSetting(nDIPGroup + j))
            {
                nCurrentSetting = i;
            }
        }
        SendMessage(GetDlgItem(hInpDIPSWDlg, IDC_INPCX1_VALUE), CB_SETCURSEL, (WPARAM)nCurrentSetting, 0);
    }
}

static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (Msg == WM_INITDIALOG)
    {

        //		EnableWindow(hScrnWnd, FALSE);

        hInpDIPSWDlg = hDlg;
        InpDIPSWInit();
        if (!kNetGame && bAutoPause)
        {
            bRunPause = 1;
        }

        WndInMid(hDlg, hScrnWnd);
        SetFocus(hDlg);											// Enable Esc=close

        return TRUE;
    }

    if (Msg == WM_CLOSE)
    {
        EnableWindow(hScrnWnd, TRUE);
        DestroyWindow(hInpDIPSWDlg);
        return 0;
    }

    if (Msg == WM_DESTROY)
    {
        InpDIPSWCancel();
        InpDIPSWExit();
        return 0;
    }

    if (Msg == WM_COMMAND)
    {
        int Id = LOWORD(wParam);
        int Notify = HIWORD(wParam);

        if (Id == IDOK && Notify == BN_CLICKED)  			// OK button
        {
            bOK = true;
            SendMessage(hDlg, WM_CLOSE, 0, 0);
            return 0;
        }
        if (Id == IDCANCEL && Notify == BN_CLICKED)  		// cancel = close
        {
            SendMessage(hDlg, WM_CLOSE, 0, 0);
            return 0;
        }

        // New DIPswitch value selected
        if (Id == IDC_INPCX1_VALUE && Notify == CBN_SELCHANGE)
        {
            BurnDIPInfo bdi = {0, 0, 0, 0, NULL};
            struct GameInp *pgi;
            int nSel = SendMessage(GetDlgItem(hInpDIPSWDlg, IDC_INPCX1_VALUE), CB_GETCURSEL, 0, 0);
            int j = 0;
            for (int i = 0; i <= nSel; i++)
            {
                do
                {
                    BurnDrvGetDIPInfo(&bdi, nDIPGroup + 1 + j++);
                }
                while (bdi.nFlags == 0);
            }
            pgi = GameInp + bdi.nInput + nDIPOffset;
            pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
            if (bdi.nFlags & 0x40)
            {
                while (BurnDrvGetDIPInfo(&bdi, nDIPGroup + 1 + j++) == 0)
                {
                    if (bdi.nFlags == 0)
                    {
                        pgi = GameInp + bdi.nInput + nDIPOffset;
                        pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
                    }
                    else
                    {
                        break;
                    }
                }
            }

            InpDIPSWListMake();
            return 0;
        }

        // New DIPswitch selected
        if (Id == IDC_INPC_RESET && Notify == BN_CLICKED)
        {

            InpDIPSWResetDIPs();

            InpDIPSWListMake();								// refresh view
            SendMessage(GetDlgItem(hInpDIPSWDlg, IDC_INPCX1_VALUE), CB_RESETCONTENT, 0, 0);
            return 0;
        }

    }

    if (Msg == WM_NOTIFY && lParam)
    {
        int Id = LOWORD(wParam);
        NMHDR *pnm = (NMHDR *)lParam;

        if (Id == IDC_INPCHEAT_LIST && pnm->code == LVN_ITEMCHANGED)
        {
            if (((NM_LISTVIEW *)lParam)->uNewState & LVIS_SELECTED)
            {
                InpDIPSWSelect();
            }
            return 0;
        }

        if (Id == IDC_INPCHEAT_LIST && pnm->code == NM_CUSTOMDRAW)
        {
            NMLVCUSTOMDRAW *plvcd = (NMLVCUSTOMDRAW *)lParam;

            switch (plvcd->nmcd.dwDrawStage)
            {
            case CDDS_PREPAINT:
            {
                SetWindowLongPtr(hInpDIPSWDlg, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW);
                return 1;
            }
            case CDDS_ITEMPREPAINT:
            {
                BurnDIPInfo bdi;
                BurnDrvGetDIPInfo(&bdi, plvcd->nmcd.lItemlParam);
                if (bdi.nFlags == 0xFD)
                {
                    plvcd->clrTextBk = RGB(0xFF, 0xDF, 0xBB);
                    SetWindowLongPtr(hInpDIPSWDlg, DWLP_MSGRESULT, CDRF_NEWFONT);
                }

                return 1;
            }
            }
        }
    }

    return 0;
}

int InpDIPSWCreate()
{
    if (bDrvOkay == 0)  									// No game is loaded
    {
        return 1;
    }

    bOK = false;

    FBADialogBox(hAppInst, MAKEINTRESOURCE(IDD_INPDIP), hScrnWnd, (DLGPROC)DialogProc);

    return 0;
}
